﻿
namespace Settings.NET.UnitTests
{
    using System;
    using System.Collections.Generic;

    using Microsoft.VisualStudio.TestTools.UnitTesting;

    using Settings.NET.UnitTests.Helpers;

    [TestClass]
    public class SettingsTests
    {        
        private ISettingsProvider settingsProvider;

        [TestInitialize]
        public void TestInitialise()
        {
            var settingsProviderFactory = new SettingsProviderFactory();
            this.settingsProvider = settingsProviderFactory.ConfigureSettingsProvider<TestSettingsInMemory>().Get();         
        }

        [TestMethod]
        public void SettingProvider_GetSettingsProvider_ProviderReturned()
        {
           Assert.IsNotNull(settingsProvider);
        }
        
        [TestMethod]
        public void Setting_GetSettingValueThatDoesntExist_NullReturned()
        {            
            var setting = this.settingsProvider.Key("Test").GetValue();
            Assert.IsNull(setting);
        }

        [TestMethod]
        public void Setting_GetSettingValueThatDoesntExitWithNoDefault_NullReturned()
        {            
            var setting = this.settingsProvider.Key("Test").GetValue();
            Assert.IsNull(setting);
        }

        [TestMethod]
        public void Setting_GetSettingValueThatDoesntExistWithDefaultStringValue_DefaultStringValueReturned()
        {            
            var setting = this.settingsProvider.Key("Test").GetValue("Hello");
            Assert.AreEqual("Hello", setting);
        }

        [TestMethod]
        public void Setting_GetSettingValueThatDoesntExistWithDefaultIntegerValue_DefaultIntegerValueReturned()
        {
            var setting = this.settingsProvider.Key("Test").GetValue(5);
            Assert.AreEqual(5, setting);
        }

        [TestMethod]
        public void Setting_SetValueAndRetrieve_ValueReturned()
        {            
            this.settingsProvider.Key("Test").SetValue(5);
            var value = this.settingsProvider.Key("Test").GetValue<int>();
            Assert.AreEqual(5, value);
        }

        [TestMethod]
        public void Setting_SetClassValueAndRetrieve_ValueReturned()
        {
            var person = new PersonClass { Name = "John", Age = 40 };
            this.settingsProvider.Key("Test").SetValue(person);
            var value = this.settingsProvider.Key("Test").GetValue<PersonClass>();            
            
            // TODO: use AutoFixture - see http://stackoverflow.com/questions/2046121/how-to-compare-two-objects-in-unit-test/2047576#2047576
            Assert.AreEqual(new { person.Name, person.Age }, new { value.Name, value.Age });            
        }

        [TestMethod]
        public void Setting_SetStructValueAndRetrieve_ValueReturned()
        {
            var person = new PersonStruct { Name = "John", Age = 40 };
            this.settingsProvider.Key("Test").SetValue(person);
            var value = this.settingsProvider.Key("Test").GetValue<PersonStruct>();
            Assert.AreEqual(person, value);
        }

        [TestMethod]
        public void Setting_SetEnumValueAndRetreive_ValueReturned()
        {
            this.settingsProvider.Key("Test").SetValue(PersonType.Employee);
            var value = this.settingsProvider.Key("Test").GetValue<PersonType>();
            Assert.AreEqual(PersonType.Employee, value);
        }

        [TestMethod]
        public void Setting_SetTimeSpanValueAndRetreive_ValueReturned()
        {
            var timeSpan = TimeSpan.FromMinutes(3);
            this.settingsProvider.Key("Test").SetValue(timeSpan);
            var value = this.settingsProvider.Key("Test").GetValue<TimeSpan>();
            Assert.AreEqual(timeSpan, value);
        }

        [TestMethod]
        public void Setting_SetDateValueAndRetrieve_ValueReturned()
        {
            var dateTime = DateTime.Now;
            this.settingsProvider.Key("Test").SetValue(dateTime);
            var value = this.settingsProvider.Key("Test").GetValue<DateTime>();
            Assert.AreEqual(dateTime, value);
        }

        [TestMethod]
        public void Setting_SetInScopeAndRetreive_ValueReturned()
        {
            this.settingsProvider.Key("Test").InScope("Test").SetValue(5);
            var value = this.settingsProvider.Key("Test").InScope("Test").GetValue<int>();
            Assert.AreEqual(5, value);
        }

        [TestMethod]
        public void Setting_SetInScopeAndRetrieveFromAnotherScope_NullReturned()
        {
            this.settingsProvider.Key("Test").InScope("Test").SetValue(5);
            var value = this.settingsProvider.Key("Test").GetValue<int?>();
            Assert.IsNull(value);
        }

        [TestMethod]
        public void Setting_SetInTwoScopesRetrieveNoScopeInGet_NullReturned()
        {
            this.settingsProvider.Key("Test").InScope("Test1").SetValue(5);
            this.settingsProvider.Key("Test").InScope("Test2").SetValue(10);
            var value = this.settingsProvider.Key("Test").GetValue<int?>();
            Assert.IsNull(value);
        }

        [TestMethod]
        public void Setting_SetInTwoScopesRetrieveFromOneScope_CorrectValueReturned()
        {
            this.settingsProvider.Key("Test").InScope("Test1").SetValue(5);
            this.settingsProvider.Key("Test").InScope("Test2").SetValue(10);
            var value = this.settingsProvider.Key("Test").InScope("Test2").GetValue<int?>();
            Assert.AreEqual(10, value);
        }

        [TestMethod]
        public void Setting_SetInOneScopeRetrieveFromInvalidScope_NullReturned()
        {
            this.settingsProvider.Key("Test").InScope("Test1").SetValue(5);
            var value = this.settingsProvider.Key("Test").InScope("Test2").GetValue<int?>();
            Assert.IsNull(value);
        }

        [TestMethod]
        public void Setting_SetInDefaultScopeRetrieveFromGlobalScope_ValueReturned()
        {
            this.settingsProvider.Key("Test").SetValue(10);
            var value = this.settingsProvider.Key("Test").InGlobalScope().GetValue<int>();
            Assert.AreEqual(10, value);
        }

        [TestMethod]
        public void Setting_SetInNullScopeRetrieveFromGlobalScope_ValueReturned()
        {
            this.settingsProvider.Key("Test").InScope(null).SetValue(10);
            var value = this.settingsProvider.Key("Test").InGlobalScope().GetValue<int>();
            Assert.AreEqual(10, value);
        }

        [TestMethod]
        public void Setting_RetrieveInvalidWithGet_HasValueIsFalse()
        {
            var setting = this.settingsProvider.Key("Test").Get<int>();
            Assert.IsFalse(setting.IsInStorage);
        }

        [TestMethod]
        public void Setting_RetrieveInvalidWithGetAndDefault_HasValueIsFalse()
        {
            var setting = this.settingsProvider.Key("Test").Get(5);
            Assert.IsFalse(setting.IsInStorage);
        }

        [TestMethod]
        public void Setting_RetrieveInvalidWithGetAndDefault_DefaultReturned()
        {
            var setting = this.settingsProvider.Key("Test").Get(5);
            Assert.AreEqual(5, setting.Value);
        }

        [TestMethod]
        public void Setting_RetrieveNullValue_GetNullValue()
        {
            this.settingsProvider.Key("Test").SetValue(null);
            var value = this.settingsProvider.Key("Test").GetValue();
            Assert.IsNull(value);
        }

        [TestMethod]
        public void Setting_RetreiveNullValueWithDefault_GetDefaultValue()
        {
            this.settingsProvider.Key("Test").SetValue(null);
            var value = this.settingsProvider.Key("Test").GetValue(5);
            Assert.AreEqual(5, value);
        }

        [TestMethod]
        public void Setting_RetrieveNullReferenceType_GetNullValue()
        {
            PersonClass person = null;
            this.settingsProvider.Key("Test").SetValue(person);
            var value = this.settingsProvider.Key("Test").GetValue();
            Assert.IsNull(value);
        }
        
        // Note: there is a bug with the XmlSerializer where lists serialized as null will be deserialized as an empty list
        // See https://connect.microsoft.com/VisualStudio/feedback/details/687096/xmlserializer-initializes-a-list-when-it-should-not
        // The solution is to not serialize as XML for null values, and just store null directly        
        [TestMethod]
        public void Setting_RetrieveCollectionType_GetNullValue()
        {
            List<int> list = null;
            this.settingsProvider.Key("Test").SetValue(list);
            var value = this.settingsProvider.Key("Test").GetValue<List<int>>();
            Assert.IsNull(value);
        }

        [TestMethod]
        public void Setting_RemoveSetting_GetNullValue()
        {
            this.settingsProvider.Key("Test").SetValue(5);
            this.settingsProvider.Key("Test").Remove();
            var value = this.settingsProvider.Key("Test").GetValue();
            Assert.IsNull(value);
        }
    }
}